package com.cf.base.leak;


import java.lang.ref.ReferenceQueue;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;

import static com.cf.base.leak.Leak.getOutTime;

/**
 * @author yangji
 */
public class ObjectWatcher {

    private Executor executor;
    private ReferenceQueue<Object> queue;
    private Map<String, ObjectReference> referenceMap;
    private Set<OnObjectLeakListener> onObjectLeakListeners;

    ObjectWatcher(Executor executor) {
        this.executor = executor;
        this.queue = new ReferenceQueue<>();
        this.referenceMap = new ConcurrentHashMap<>();
        onObjectLeakListeners = new HashSet<>();
    }

    public void addOnObjectLeakListeners(OnObjectLeakListener onObjectLeakListeners) {
        this.onObjectLeakListeners.add(onObjectLeakListeners);
    }

    public void addWatcher(Object object) {
        addWatcher(object, getOutTime());
    }

    public void addWatcher(Object object, long time) {
        time = Math.max(time, getOutTime());
        String key = UUID.randomUUID().toString();
        ObjectReference reference = new ObjectReference(key, object, queue);
        referenceMap.put(key, reference);
        executor.execute(new LeakRunnable(key, time) {
            @Override
            public void run() {
                //验证是否回收
                String key = getKey();
                checkLeak(key);
            }
        });
    }

    /**
     * 根据Key 监测是否回收
     *
     * @param key uuid Key
     */
    private void checkLeak(String key) {
        clearMemory();
        ObjectReference reference = referenceMap.get(key);
        if (reference != null) {
            reference.leak();
            // 通知处理 已经泄露了
            onObjectLeakListeners.forEach(OnObjectLeakListener::objectLeak);
        }
        // 已经回收了
    }

    /**
     * 清理已经回收的内存
     */
    private void clearMemory() {
        ObjectReference reference = null;
        do {
            reference = (ObjectReference) queue.poll();
            if (reference != null) {
                //已经回收了
                referenceMap.remove(reference.getKey());
            }
        } while (reference != null);
    }



}
